﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Xml;

namespace Fadd.Components
{
	/// <summary>
	/// Reads configuration from app.config.
	/// </summary>
	class ConfigSectionHandler : IConfigurationSectionHandler 
	{
		/// <summary>Creates a configuration section handler.</summary>
		/// <param name="parent">Parent object.</param>
		/// <param name="configContext">Configuration context object.</param>
		/// <param name="section">Section XML node.</param>
		/// <returns>The created section handler object.</returns>
		/// <exception cref="ConfigurationErrorsException">If configuration is incorrect.</exception>
		public object Create(object parent, object configContext, XmlNode section)
		{
			List<Component> components = new List<Component>();
			foreach (XmlNode node in section.ChildNodes)
			{
				string interfaceTypeName = GetAttribute(node, "Interface", true);
				string instanceTypeName = GetAttribute(node, "Instance", true);
				string runAt = GetAttribute(node, "RunAt", false);
				bool isSingleton = GetAttribute(node, "IsNotSingleton", false) == "true";
				string versionStr = GetAttribute(node, "Version", false);
				int version = 0;
				if (versionStr != null)
				{
					try
					{
						version = int.Parse(versionStr);
					}
					catch (FormatException err)
					{
						throw new ConfigurationErrorsException("Version attribute of " + instanceTypeName + " is not a number.", err);
					}
				}

				Type interfaceType = Type.GetType(interfaceTypeName);
				Type instanceType = Type.GetType(instanceTypeName);

				Component component = new Component
				{
					InstanceType = instanceType,
					InterfaceType = interfaceType,
					IsNotSingleton = isSingleton,
					RunAt = runAt,
					Version = version,
				};

				foreach (XmlNode parameterNode in node.ChildNodes)
				{
					string name = GetParameterAttribute(parameterNode, "Name", true);
					string valueType = GetParameterAttribute(parameterNode, "Type", false);
					object value = parameterNode.FirstChild.InnerText;
					if (valueType != null)
					{
						Type type = Type.GetType(valueType);
						if (type == null)
							throw new ConfigurationErrorsException("Failed to find specified type for parameter '" + name + "' of component '" + instanceTypeName + "'.");
						try
						{
							value = Convert.ChangeType(value, type);
						}
						catch(InvalidCastException err)
						{
							throw new ConfigurationErrorsException("Failed to convert parameter '" + name + "' of component '" + instanceTypeName +
							                                 "' to '" + valueType + "'.", err);
						}
					}
						
					component.KnownParameters.Add(name, value);
				}

				components.Add(component);
			}
			return components;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="node"></param>
		/// <param name="attribute"></param>
		/// <param name="isMandatory"></param>
		/// <returns></returns>
		/// <exception cref="ConfigurationErrorsException"><c>ConfigurationException</c>.</exception>
		private string GetAttribute(XmlNode node, string attribute, bool isMandatory)
		{
			XmlAttribute aattribute = node.Attributes[attribute];
			if (aattribute == null && isMandatory)
				throw new ConfigurationErrorsException("Attribute '" + attribute + "' is mandatory for Component configuration.");
			if (aattribute == null)
				return null;
			return aattribute.InnerText;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="node"></param>
		/// <param name="attribute"></param>
		/// <param name="isMandatory"></param>
		/// <returns></returns>
		/// <exception cref="ConfigurationErrorsException"><c>ConfigurationException</c>.</exception>
		private string GetParameterAttribute(XmlNode node, string attribute, bool isMandatory)
		{
			XmlAttribute aattribute = node.Attributes[attribute];
			if (aattribute == null && isMandatory)
				throw new ConfigurationErrorsException("Attribute '" + attribute + "' is mandatory for Components parameter configuration.");
			if (aattribute == null)
				return null;
			return aattribute.InnerText;
		}
	}
}
